package ${(projectConfig.projectPackageName)!}.common.security.filter;

import cn.aezo.utils.base.ExceptionU;
import ${(projectConfig.projectPackageName)!}.common.security.SpringSecurityConfig;
import ${(projectConfig.projectPackageName)!}.common.security.jwt.SecurityJwtTokenUtils;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private String token_header = "X-Token";

    @Resource
    private SecurityJwtTokenUtils securityJwtTokenUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if(!SpringSecurityConfig.Login_Uri.equals(request.getRequestURI())) {
            String authToken = request.getHeader(this.token_header);
            if(StringUtils.isEmpty(authToken)) {
                chain.doFilter(request, response);
                return;
            }

            try {
                String username = securityJwtTokenUtils.getUsernameFromToken(authToken);
                if(username == null)
                    throw new ExceptionU.AuthTokenInvalidException();

                logger.info(String.format("Checking authentication for user %s. ExpirationDate: %s.",
                    username, securityJwtTokenUtils.getExpirationDateFromToken(authToken)));

                if (SecurityContextHolder.getContext().getAuthentication() == null) {
                    // It is not compelling necessary to load the use details from the database. You could also store the information
                    // in the token and read it from it. It's up to you ;)
                    // UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                    UserDetails userDetails = securityJwtTokenUtils.getUserFromToken(authToken);

                    // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
                    // the database compellingly. Again it's up to you ;)
                    if (securityJwtTokenUtils.validateToken(authToken, userDetails)) {
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken
                            (userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        logger.info(String.format("Authenticated user %s, setting security context", username));
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            } catch (SignatureException e) {
                throw new ExceptionU.AuthTokenInvalidException();
            }
        }

        chain.doFilter(request, response);
    }

}